<script setup>

import { computed, reactive, ref } from 'vue';
import * as math from 'mathjs';
import * as utils from './utils';

const points = ref([
  [100, 100],
  [100, 200],
  [200, 200],
  [200, 100],
])

// 将点位数据转成转成适合应用变换的矩阵形式
/**
 * [
 *  [100,100],
 *  [100,200],
 *  [200,200],
 *  [200,100],
 * ]
 * 
 * ---> 转换后
 * 
 * [
 *  [100,100,200,200],
 *  [100,200,200,100],
 *  [1  ,1  ,1  ,1],
 * ]
 * 
 * 原本每个点数据是一行的，转换后每个点数据后面增加了1个维度，而且由行向量转成了列向量
 */
function points2Matrix(table) {
  // 定义原始矩阵
  const originalMatrix = math.matrix(table);
  // 获取矩阵的行数
  const rows = originalMatrix.size()[0];
  // 创建一列全为 1 的矩阵
  const onesColumn = math.ones(rows, 1);
  // 将全为 1 的列添加到原始矩阵的右侧
  const newMatrix = math.concat(originalMatrix, onesColumn, 1);
  // 对新矩阵进行转置操作
  return math.transpose(newMatrix);
}

// 数据或者变换矩阵变化后，更新视图
const pointsComputed = computed(() => {
  const pointsMatrix = points2Matrix(points.value)

  // 应用的变换矩阵（平移中或者旋转中需要临时的变换矩阵）
  let matrix = transformationMatrix.value;
  if (dragObj.flag) matrix = dragObj.transformationMatrix
  else if (rotateObj.flag) matrix = rotateObj.transformationMatrix

  // 将原始数据应用变换矩阵后得到最终的可视化数据
  const resultMatrix = math.transpose(math.multiply(matrix, pointsMatrix))
  return resultMatrix.toArray().map(([x, y]) => `${x},${y}`).join(" ")
})

// 原始变换矩阵
const transformationMatrix = ref(math.matrix(
  [
    [1, 0, 0],
    [0, 1, 0],
    [0, 0, 1],
  ]
));

// svg可视区域的大小
const svg = reactive({
  width: 1000,
  height: 500,
})
const center = computed(() => {
  return { cx: svg.width / 2, cy: svg.height / 2 }
})

// 缩放
function wheel({ deltaY, offsetX, offsetY }) {
  if (deltaY > 0) {
    transformationMatrix.value = utils._scale(transformationMatrix.value, 0.9, offsetX, offsetY)
  } else {
    transformationMatrix.value = utils._scale(transformationMatrix.value, 1.1, offsetX, offsetY)
  }
}


// 拖动
const dragObj = reactive({
  flag: false,
  offsetX: 0,
  offsetY: 0,
  transformationMatrix: null,
})

function moveStart({ offsetX, offsetY }) {
  Object.assign(dragObj, { flag: true, offsetX, offsetY, transformationMatrix: transformationMatrix.value })
}

function moveEnd({ offsetX, offsetY }) {
  const { offsetX: originalOffsetX, offsetY: originalOffsetY } = dragObj
  transformationMatrix.value = utils._translation(transformationMatrix.value, offsetX - originalOffsetX, offsetY - originalOffsetY)
  Object.assign(dragObj, { flag: false, offsetX: 0, offsetY: 0, transformationMatrix: null })
}

// 旋转
const rotateObj = reactive({
  flag: false,
  offsetX: 0,
  offsetY: 0,
  transformationMatrix: null,
})
function rotateStart({ offsetX, offsetY }) {
  Object.assign(rotateObj, { flag: true, offsetX, offsetY, transformationMatrix: transformationMatrix.value })
}
function rotateEnd({ offsetX, offsetY }) {
  const { cx, cy } = center.value
  const { offsetX: originalOffsetX, offsetY: originalOffsetY } = rotateObj
  const angle = utils._rotateAngle(cx, cy, originalOffsetX, originalOffsetY, offsetX, offsetY)
  transformationMatrix.value = utils._rotate(transformationMatrix.value, angle, cx, cy)
  Object.assign(rotateObj, { flag: false, offsetX: 0, offsetY: 0, transformationMatrix: null })
}


// 移动中
function moving({ offsetX, offsetY }) {
  if (dragObj.flag) {
    const { offsetX: originalOffsetX, offsetY: originalOffsetY } = dragObj
    dragObj.transformationMatrix = utils._translation(transformationMatrix.value, offsetX - originalOffsetX, offsetY - originalOffsetY)
  } else if (rotateObj.flag) {
    const { cx, cy } = center.value
    const { offsetX: originalOffsetX, offsetY: originalOffsetY } = rotateObj
    const angle = utils._rotateAngle(cx, cy, originalOffsetX, originalOffsetY, offsetX, offsetY)
    rotateObj.transformationMatrix = utils._rotate(transformationMatrix.value, angle, cx, cy)
  }
}
</script>

<template>
  <div>
    <svg @wheel="wheel" @mousedown.left="moveStart" @mousemove="moving" @mouseup.left="moveEnd"
      @mousedown.right="rotateStart" @mouseup.right="rotateEnd" @contextmenu.prevent :style="svg">
      <!-- 中间参考点 -->
      <circle :cx='center.cx' :cy='center.cy' r="5" fill="red"></circle>

      <!-- 用点画出来的多边形，方便进行旋转缩放移动效果的查看 -->
      <polygon :points="pointsComputed" fill="transparent" stroke="black"></polygon>

    </svg>

    <!-- 变换矩阵 -->
    <table border="1">
      <tr v-for="row in transformationMatrix.toArray()">
        <td v-for="cell in row">{{ cell.toFixed(4) }}</td>
      </tr>
    </table>
  </div>
</template>

<style scoped>
svg {
  background-color: beige;
}
</style>
